#ifndef __GlDistributor_h__
#define __GlDistributor_h__

#include <algorithm>
#include <vector>
#include "GlId.h"
#include "GlMain.h"

namespace GraphicLibrary
{
	///
	/// This class is intended to be a base class for distribute informations to listeners.
	/// The main solution is that it stores the listeners and the listeners' ids. When a listener leaves
	/// it removes the listener from the array but leaves a null pointer in it's place. So that it will
	/// not alters the structure of the listeners. The main goal is that if the listener disconnects from
	/// the disributor in response to the distributors message than the distributing process do not have
	/// to be interrupted. The null pointer will be removed from the structure if someone can be placed
	/// over it.
	/// @param ListenerType must be a descendant of Listener.
	template<typename ListenerType>
		class Distributor
			: public Id
		{
			public:
				/// Connects listener to the distributor. This function is const so that a listener can connect to a const distributor.
				/// This function calls `connectedTo(const Distributor *distributor)` function on the listener if the distributor accepts the connection attempt
				/// and the listener is not already listening to the distributor.
				/// @param listener Listener to connect.
				inline void connect(ListenerType *listener) const;
				/// Disconnects listener from the distributor. This function is const so that a listener can disconnect from a const distributor.
				/// This function calls `disconnectedFrom(const MyDistributor *distributor)` function on the listener. If listener is not conencted to the distributor
				/// than this function has no effect and `disconnectedFrom(const MyDistributor *distributor)` is not be called.
				/// @param listener Listener to disconnect.
				inline void disconnect(ListenerType *listener) const;
				/// In some rare cases (usually when you connect lots of listeners to the distributor at once and than disconnect many of them)
				/// it could happen that there is lots of null pointer in the listener array.
				/// In this case a distribution procedure could be slow. This function removes null pointers by telescoping the valuable elements.
				inline void optimize() const;
				///
				/// Calls disconnectAll().
				virtual ~Distributor();
			protected:
				/// Called when a listener is trying to connect.
				/// Override it if you want custom policy for connections.
				/// @return Returns true if the listener can connect.
				virtual bool beforeListenerConnected(ListenerType *listener) const;
				///
				/// Called after a listener is disconnected.
				virtual void afterListenerDisconnected(ListenerType *listener) const;
				inline void disconnectAll() const;
				/// Use this function to distribute message to listeners.
				/// @param message This is the message type. The message should be a callable type and it has two parameters.
				/// First parameter must be a type of `ListenerType*`. Second parameter must be the Distributor's with const extension own type.
				/// like: `void foe(ListenerType *listener, const MyDistributor *dist);` Use boost to call member function on listener like:\n
				/// `class ADist;`\n
				/// `class AListener :`\n
				/// `    public GraphicLibrary::Listener<ADist>`\n
				/// `{`\n
				/// `    public:`\n
				/// `        void foe(const MyDistributor *dist, const int a);`\n
				/// `};`\n
				/// \n
				/// `class ADist`\n
				/// `    : public GraphicLibrary::Distributor<AListener>`\n
				/// `{`\n
				/// `    public:`\n
				/// `        void sendMessage()`\n
				/// `        {`\n
				/// `            distribute(boost::bind(&AListener::foe, _1, _2, 10));`\n
				/// `        }`\n
				/// `};`
				template<typename MessageType>
					inline void distribute(const MessageType &message) const
					{
						for(ListenerArray::iterator i = mListeners.begin(); i != mListeners.end(); ++i)
							if(i->mListener)
								message(i->mListener, this);
								//message(i->mListener, (const ListenerType::MyDistributor*)this);
								//message(i->mListener, static_cast<const ListenerType::MyDistributor*>(this));
					}

				///
				/// This class in intended for easy call of simple listener function.
				class Caller
				{
					public:
						typedef void (ListenerType::*CallType)(const Distributor*);
						inline Caller(CallType func)
							: mCall(func)
						{}
						inline void operator () (ListenerType *listener, const Distributor *caller) const	{(listener->*mCall)(caller);}
					private:
						CallType	mCall;
				};

				template<typename FirstParamType>
					class Caller1
					{
						public:
							typedef void (ListenerType::*CallType)(const Distributor*, const FirstParamType);
							inline Caller1(CallType func, const FirstParamType firstParam)
								: mCall(func)
								, mFirstParam(firstParam)
							{}
							inline void operator () (ListenerType *listener, const Distributor *caller) const	{(listener->*mCall)(caller, mFirstParam);}
						private:
							CallType				mCall;
							const FirstParamType	mFirstParam;
					};

				template<typename FirstParamType, typename SecondParamType>
					class Caller2
					{
						public:
							typedef void (ListenerType::*CallType)(const Distributor*, const FirstParamType, const SecondParamType);
							inline Caller2(CallType func, const FirstParamType firstParam, const SecondParamType secondParam)
								: mCall(func)
								, mFirstParam(firstParam)
								, mSecondParam(secondParam)
							{}
							inline void operator () (ListenerType *listener, const Distributor *caller) const	{(listener->*mCall)(caller, mFirstParam, mSecondParam);}
						private:
							CallType				mCall;
							const FirstParamType	mFirstParam;
							const SecondParamType	mSecondParam;
					};

				template<typename FirstParamType, typename SecondParamType, typename ThirdParamType>
					class Caller3
					{
						public:
							typedef void (ListenerType::*CallType)(const Distributor*, const FirstParamType, const SecondParamType, const ThirdParamType);
							inline Caller3(CallType func, const FirstParamType firstParam, const SecondParamType secondParam, const ThirdParamType thirdParam)
								: mCall(func)
								, mFirstParam(firstParam)
								, mSecondParam(secondParam)
								, mThirdParam(thirdParam)
							{}
							inline void operator () (ListenerType *listener, const Distributor *caller) const	{(listener->*mCall)(caller, mFirstParam, mSecondParam, mThirdParam);}
						private:
							CallType				mCall;
							const FirstParamType	mFirstParam;
							const SecondParamType	mSecondParam;
							const ThirdParamType	mThirdParam;
					};
			private:
				class IdListenerPair
				{
					public:
						enum TagNullPointer
						{
							NullPointer
						};

						Id::IdType		mId;
						ListenerType*	mListener;
						inline IdListenerPair();
						inline IdListenerPair(ListenerType *listener);
						///
						/// IdListenerPair p;\n
						/// ...\n
						/// p == IdListenerPair::NullPointer;
						/// returns true if p.mLisetner is null.
						/// @return Returns true if mListener is null.
						inline bool operator == (const TagNullPointer);

						/// This class serves as a less operator on IdListenerPair objects.
						/// This makes a simple mId comparison.
						class Less
						{
							public:
								inline bool operator () (const IdListenerPair &left, const IdListenerPair &right) const;
								inline bool operator () (const IdListenerPair &left, const Id::IdType right) const;
								inline bool operator () (const Id::IdType left, const IdListenerPair &right) const;
						};
				};
				typedef std::vector<IdListenerPair>	ListenerArray;
#pragma warning(disable: 4251)
				mutable ListenerArray	mListeners;	/// This member stores the connected listeners. It is mutable so that a listener can connect or disconnecto to a const Distributor.
#pragma warning(default: 4251)
		};
}

#include "GlDistributor.inl"

#endif // __GlDistributor_h__
